package cn.tedu.justone.pack02;

/**
 * @作者：JustOne
 * @时间：2021/8/22 - 15:27
 */
public class Demo04_初识synchronized {
    public static void main(String[] args) {

        //初识synchronized
        //初识synchronized
        //初识synchronized

        //先看示例：
        // 请看本例，两个线程访问同一个资源时，由于大家是异步方访问(各干各的)，
        // 就可能会引了共享数据的混乱，例如两人买到同一张票、票的库存变为负数等，
        // 原因是因为时间片分配的随机性所导致的，这就是所谓的线程安全问题。

        //解决方案：synchronized
        // 造成线程安全问题的主要原因有两点，一是存在共享数据(也称临界资源)，
        // 二是存在多条线程操作共享数据。
        // 我们可以使用synchronized关键字保证在同一个时刻只有一个线程可以执行
        // 某个方法或者代码块，其他线程必须等到该线程处理完数据后再进行，这种方式
        // 有个高尚的名称叫互斥锁。
        //

        //synchronized的三种应用方式
        // 1,修饰实例方法，锁对象是当前实例
        // 2,修饰静态方法，锁对象是当前类对象
        // 3,修饰代码块，锁对象由我们人为规定

        Plate04 plate = new Plate04();

        Thread girl = new Thread(){
            @Override
            public void run() {
                while (plate.getMi()>0){
                    plate.eatMi();
                }
            }
        };

        Thread boy = new Thread(){
            @Override
            public void run() {
                while (plate.getMi()>0){
                    plate.eatMi();
                }
            }
        };

        girl.setName("女生");
        boy.setName("男生");

        girl.start();
        boy.start();

    }
}

class Plate04 {
    private static int mi = 5;//请声明为static，确保保有一份

    // 加了synchronized的方法称为同步方法。
    // 同步方法的特点是当A线程执行该方法时该方法会被上锁，别的方法只能等待。
    // 当A线程执行完该方法时才会释放(解除)这个锁，别的线程会来竞争这个锁。
    // 本方法是实例方法，所以锁对象是当前类的实例。

    //public synchronized void eatMi(){
    public void eatMi(){
        if(mi>0){
            mySleep(100);
            System.out.println(Thread.currentThread().getName()+"在吃第"+mi+"颗");
            this.mi--;
            System.out.println("余数是：" + mi);
        }else{
            System.out.println("已售罄");
        }
    }

    public int getMi(){
        return mi;
    }

    //为了让代码更优雅，编写统一的休眠方法
    public static void mySleep(long m){
        try {
            Thread.sleep(m);
        } catch (InterruptedException e) {
            System.out.println("You：谁喊我？");
            e.printStackTrace();
        }
    }
}
